package com.mfs.proxy.client.transfer.http.request;

import com.mfs.common.constant.Constant;
import com.mfs.common.utills.CollectionUtil;
import com.mfs.common.utills.StringUtil;
import com.mfs.proxy.client.transfer.http.ByteBufferUtil;
import com.mfs.proxy.core.client.config.ClientConfig;
import com.mfs.proxy.core.client.processor.Transfer;
import com.mfs.proxy.core.exceptions.ProxyException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Http请求转换器
 *
 * @author mfs
 * @date 2023/7/20 21:08
 **/
public class HttpRequestTransfer implements Transfer {
    private final static Logger logger = LoggerFactory.getLogger(HttpRequestTransfer.class);
    private List<HttpRequest> resolvedRequests = new ArrayList<>();
    private HttpRequest resolvingRequest = null;
    private ByteBuffer leftByteBuffer = null;
    private ResolveStepEnum nextResolveStep = ResolveStepEnum.RESOLVE_BEGIN;
    @Override
    public void translate(ByteBuffer byteBuffer) {
        ByteBuffer mergedByteBuffer = ByteBuffer.allocate(byteBuffer.capacity() + (leftByteBuffer == null ? 0 : leftByteBuffer.capacity()));
        if (leftByteBuffer != null) {
            mergedByteBuffer.put(leftByteBuffer);
        }
        mergedByteBuffer.put(byteBuffer);
        mergedByteBuffer.flip();
        boolean end = false;
        while (!end) {
            switch (nextResolveStep){
                case RESOLVE_BEGIN:
                    resolvingRequest = new HttpRequest();
                    nextResolveStep = ResolveStepEnum.RESOLVE_REQUEST_LINE;
                    break;
                case RESOLVE_REQUEST_LINE:
                    if (resolveRequestLine(resolvingRequest.getHttpRequestLine(), mergedByteBuffer)) {
                        leftByteBuffer = ByteBuffer.wrap(byteBuffer.array(), mergedByteBuffer.position(), mergedByteBuffer.limit() - mergedByteBuffer.position());
                        end = true;
                    } else {
                        nextResolveStep = ResolveStepEnum.RESOLVE_REQUEST_HEDA;
                    }
                    break;
                case RESOLVE_REQUEST_HEDA:
                    if (resolveRequestHeader(resolvingRequest.getHttpRequestHeader(), mergedByteBuffer)) {
                        leftByteBuffer = ByteBuffer.wrap(byteBuffer.array(), mergedByteBuffer.position(), mergedByteBuffer.limit() - mergedByteBuffer.position());
                        end = true;
                    } else if(resolvingRequest.getHttpRequestHeader().isCompleted()) {
                        nextResolveStep = ResolveStepEnum.RESOLVE_REQUEST_BODY;
                    }
                    break;
                case RESOLVE_REQUEST_BODY:
                    // 暂不解析,仅字节赋值，需要时调用请求的decode方法解析
                    String bodyLength = resolvingRequest.getHttpRequestHeader().getHead("content-length");
                    if (StringUtil.isInt(bodyLength)) {
                        int length = Integer.valueOf(bodyLength);
                        if (mergedByteBuffer.limit() - mergedByteBuffer.position() < length) {
                            leftByteBuffer = ByteBuffer.wrap(byteBuffer.array(), mergedByteBuffer.position(), mergedByteBuffer.limit() - mergedByteBuffer.position());
                            end = true;
                        } else {
                            byte[] body = new byte[length];
                            mergedByteBuffer.get(body);
                            resolvingRequest.getHttpRequestBody().setBytes(body);
                            nextResolveStep = ResolveStepEnum.RESOLVE_END;
                        }
                    }
                    break;
                case RESOLVE_END:
                    resolvedRequests.add(transfer(resolvingRequest));
                    resolvingRequest = null;
                    nextResolveStep = ResolveStepEnum.RESOLVE_BEGIN;
                    break;
            }
        }
    }

    private HttpRequest transfer(HttpRequest httpRequest) {
        return proxyAddressReplace(httpRequest);
    }

    /**
     * 使用目标地址替换请求行、请求头中的代理地址
     * */
    private HttpRequest proxyAddressReplace(HttpRequest httpRequest) {
        String proxyAddress = ClientConfig.getInstance().getProxyHandle().getProxyIp() + Constant.COLON + ClientConfig.getInstance().getProxyHandle().getProxyPort();
        String targetAddress = ClientConfig.getInstance().getTargetIp() + Constant.COLON + ClientConfig.getInstance().getTargetPort();
        httpRequest.getHttpRequestLine().setTarget(httpRequest.getHttpRequestLine().getTarget().replaceAll(proxyAddress, targetAddress));

        httpRequest.getHttpRequestHeader().heads().stream().forEach(headName -> {
            String headValue = httpRequest.getHttpRequestHeader().getHead(headName);
            httpRequest.getHttpRequestHeader().putHead(headName, headValue.replaceAll(proxyAddress, targetAddress));
        });
        return httpRequest;
    }


    /**
     * 解析请求行
     * @return true, 剩余的字节已经无法完整的解析为一行，false，剩余的部分仍可继续解析
     */
    private boolean resolveRequestLine(HttpRequestLine httpRequestLine, ByteBuffer byteBuffer) {
        byte[] requestLineByte = ByteBufferUtil.getALine(byteBuffer);
        if (requestLineByte == null) {
            return true;
        }

        try {
            String requestLineString = new String(requestLineByte, "ascii");
            String[] split = requestLineString.split("\\s+");
            if (split.length < 3) {
                logger.error("错误的HTTP请求行{}", Arrays.toString(requestLineByte));
                throw new ProxyException("错误的HTTP请求行");
            }
            httpRequestLine.setMethod(split[0]);
            httpRequestLine.setTarget(split[1]);
            httpRequestLine.setVersion(split[2].replace("\r\n", ""));
            return false;
        } catch (UnsupportedEncodingException e) {
        }
        return false;
    }

    /**
     * 解析请求头
     * @return true, 剩余的字节已经无法完整的解析为一行，false，剩余的部分仍可继续解析
     */
    private boolean resolveRequestHeader(HttpRequestHeader httpRequestHeader, ByteBuffer byteBuffer) {
        byte[] aHeadByte = ByteBufferUtil.getALine(byteBuffer);
        if (aHeadByte == null) {
            return true;
        }

        try {
            String aHeadString = new String(aHeadByte, "ascii");
            if (aHeadString.length() > 1 && aHeadString.charAt(0) == '\r' && aHeadString.charAt(1) == '\n') {
                httpRequestHeader.setCompleted(true);
                return false;
            }
            String[] split = aHeadString.split(":\\s", 2);
            if (split.length < 2) {
                logger.error("错误的HTTP请求头{}", Arrays.toString(aHeadByte));
                throw new ProxyException("错误的HTTP请求头");
            }
            httpRequestHeader.putHead(split[0].toLowerCase(), split[1].replace("\r\n", ""));
            return false;
        } catch (UnsupportedEncodingException e) {
        }
        return false;
    }

    @Override
    public boolean hasNext() {
        return CollectionUtil.isNotEmpty(resolvedRequests);
    }

    @Override
    public ByteBuffer next() {
        return hasNext() ? ByteBuffer.wrap(resolvedRequests.remove(0).getBytes()) : null;
    }

    enum ResolveStepEnum {
        RESOLVE_BEGIN,
        RESOLVE_REQUEST_LINE,
        RESOLVE_REQUEST_HEDA,
        RESOLVE_REQUEST_BODY,
        RESOLVE_END;
    }
}
